<?php
/**************************************************************************
 *
 *   Copyright 2010 American Public Media Group
 *
 *   This file is part of AIR2.
 *
 *   AIR2 is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   AIR2 is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with AIR2.  If not, see <http://www.gnu.org/licenses/>.
 *
 *************************************************************************/

require_once 'AIR2_Controller.php';

/**
 * Directory Controller (displays tree of users and organizations)
 *
 * @author rcavis
 * @package default
 */
class Directory_Controller extends AIR2_Controller {

    /**
     * Load required libraries
     */
    public function begin() {
        parent::begin();
        $this->load->library('AirQuery'); //lib to execute doctrine queries
    }


    /**
     * Starting point for any requests to "air2/directory"
     */
    public function index() {
        if ($this->response_view == 'html') {
            // load inline-data into html view
            $data = $this->_get_html_data();
            $body = $this->load->view('directory', $data, true);
            $this->response(
                array(
                    'head' => array('title' => AIR2_SYSTEM_DISP_NAME.' - User Directory'),
                    'body' => $body,
                )
            );
        }
        else {
            // raw data view
            $data = $this->_get_raw_data();
            $this->response($data);
        }
    }


    /**
     * Get inline directory data for an html-view
     *
     * @return array data to load inline
     */
    private function _get_html_data() {
        // get inline organization data
        $conn = AIR2_DBManager::get_connection();
        $ids = $conn->fetchColumn('select org_id from organization '.
            'where org_parent_id is null', array(), 0);
        $direc = $this->_fetch_orgs($ids, false);

        // get inline user data
        $q = Doctrine_Query::create()->from('User');
        $q->orderBy('user_last_name asc');
        $q->addWhere('user_type != ?', User::$TYPE_SYSTEM);
        $q->limit(5);
        $users = $this->airquery->execute_query($q, array());

        // get inline organization data
        $q = Doctrine_Query::create()->from('Organization');
        $q->limit(5);
        $orgs = $this->airquery->execute_query($q, array());

        // get inline stats data TODO!
        $stats = $this->_get_stats_data();

        return array(
            'direc' => $direc,
            'users' => $users,
            'orgs'  => $orgs,
            'stats' => $stats,
        );
    }


    /**
     * Get inline statistics data
     *
     * @return array
     */
    private function _get_stats_data() {
        $conn = AIR2_DBManager::get_connection();

        // fetch some stats data
        $usr_count = $conn->fetchColumn('select count(*) from user', array(), 0);
        $org_count = $conn->fetchColumn('select count(*) from organization', array(), 0);
        $usrs = AIR2_Query::create()->from('User u')
        ->leftJoin('u.UserOrg uo WITH uo.uo_home_flag = true')
        ->leftJoin('uo.Organization o')
        ->orderBy('user_cre_dtim desc')
        ->limit(3)
        ->fetchArray();
        $user = 'select count(*) from user_org where uo_org_id = org_id';
        $orgs = AIR2_Query::create()->from('Organization o')
        ->select("o.*, ($user) as user_count")
        ->orderBy('o.org_cre_dtim desc')
        ->limit(3)
        ->fetchArray();

        return array(
            'radix' => array(
                'stats_id' => 1,
                'user_count' => $usr_count,
                'org_count' => $org_count,
                'new_users' => $usrs,
                'new_orgs' => $orgs,
            ),
            'metaData'=> $this->_create_metadata_basic(
                'stats_id',
                array( // column definitions
                    array('name'=>'stats_id'),
                    array('name'=>'user_count'),
                    array('name'=>'org_count'),
                    array('name'=>'new_users'),
                    array('name'=>'new_orgs'),
                )
            ),
        );
    }


    /**
     * Get raw data to return for json/xml/etc view.  This also processes valid
     * GET parameters.
     *
     * @return array data to load inline
     */
    private function _get_raw_data() {
        $ids = null;
        $list = true;
        $user = true;

        // get any valid GET args
        $all = $this->input->get('all', true);
        $org_uuid = $this->input->get('org_uuid', true);

        // check query args to determine which id's to fetch
        $conn = AIR2_DBManager::get_connection();
        if ($all) {
            $ids = $conn->fetchColumn('select org_id from organization', array(), 0);
        }
        elseif ($org_uuid) {
            $u = $org_uuid;
            $ids = $conn->fetchColumn('select org_id from organization '.
                'where org_uuid = ? or org_parent_id in (select org_id '.
                'from organization where org_uuid = ?)', array($u, $u), 0);
            $list = false;
        }
        else {
            $ids = $conn->fetchColumn('select org_id from organization '.
                'where org_parent_id is null', array(), 0);
            $user = false;
        }

        if (isset($_GET['user'])) {
            if (is_string($this->input->get('user', true)))
                $user = $this->input->get('user', true);
            else
                $user = $this->input->get('user', true) ? true : false;
        }

        return $this->_fetch_orgs($ids, $user, $list);
    }


    /**
     * Fetch organizations (along with users) in a tree format.  Returns data
     * formatted by AirQuery lib.
     *
     * @param array   $org_ids  (optional) ids to load.  Empty or null will return all
     * @param boolean|string $incl_usr include users, and optionally search
     * @param boolan  $list     (optional) return list (instead of single) as 'radix'
     * @return array data formatted by AirQuery
     */
    private function _fetch_orgs($org_ids=null, $incl_usr=true, $list=true) {
        $q = AIR2_Query::create()->from('Organization o')->orderBy('o.org_display_name asc');

        // NOTE: we have to trick doctrine and create a "tmp_id", to avoid
        // alias conflicts from the parser on the subqueries
        $child = 'select count(*) from organization o where org_parent_id = tmp_id';
        $user = 'select count(*) from user_org where uo_org_id = tmp_id';
        $q->select("o.*, o.org_id as tmp_id, ($child) as child_count, ($user) as user_count");

        // optionally restrict the organizations to return
        if ($org_ids && count($org_ids) > 0) {
            $q->whereIn('org_id', $org_ids);
        }

        if ($incl_usr) {
            $q->addSelect("uo.*, ar.*, u.*, em.*, ph.*");
            $q->leftJoin("o.UserOrg uo");
            $q->leftJoin("uo.AdminRole ar");
            $q->leftJoin("uo.User u");
            $q->leftJoin("u.UserEmailAddress em");
            $q->leftJoin("u.UserPhoneNumber ph");
            $q->addOrderBy("u.user_first_name asc");

            if (is_string($incl_usr)) {
                User::add_search_str($q, 'u', $incl_usr);
            }
        }

        // fetch the data and assemble it into an organization tree
        $radix = $q->fetchArray();
        $tree = $this->_assemble_tree($radix);

        // process the data with AirQuery
        $this->airquery->extra_field_defs[] = array('name' => 'child_count');
        $this->airquery->extra_field_defs[] = array('name' => 'user_count');
        $this->airquery->process_radix($tree);
        $data = $this->airquery->format_output($tree, $list, $q, true);

        // if in single-view (not list), close other branches
        if (!$list && isset($data['radix']['children'])) {
            foreach ($data['radix']['children'] as &$o) {
                unset($o['UserOrg']);
            }
        }

        // add extra metadata fields
        $data['metaData']['fields'][]= array('name' => 'children');

        return $data;
    }


    /**
     * Helper function to re-assemble a list of organizations into a tree.  The
     * passed list MUST only contain orgs that are connected in the tree!  (Or
     * orgs that have no parent).
     *
     * @param array   $radix (reference) list of organizations
     * @return array tree of organizations
     */
    private function _assemble_tree(&$radix) {
        // create a lookup array
        $lookup = array();
        foreach ($radix as &$org) {
            $lookup[$org['org_id']] = &$org;
        }

        // place each item under it's parent
        $tree = array();
        foreach ($radix as &$org) {
            $pid = isset($org['org_parent_id']) ? $org['org_parent_id'] : null;
            if ($pid && isset($lookup[$pid])) {
                if (!isset($lookup[$pid]['children']))
                    $lookup[$pid]['children'] = array();
                $lookup[$pid]['children'] []= &$org;
            }
            else {
                $tree []= &$org;
            }
        }

        return $tree;
    }


}
